package com.songlanyun.modules.marketing.seckill.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.songlanyun.common.config.SemaphoreTool;
import com.songlanyun.common.enums.LockPrefixConstant;
import com.songlanyun.common.enums.MarketingConstant;
import com.songlanyun.common.exception.RRException;
import com.songlanyun.common.utils.*;
import com.songlanyun.common.utils.custom.DateCustomUtils;
import com.songlanyun.common.utils.custom.MathUtils;
import com.songlanyun.common.utils.custom.SemaphoreUtils;
import com.songlanyun.modules.account.model.vo.AccountVO;
import com.songlanyun.modules.exception.MarketingException;
import com.songlanyun.modules.goods.entity.Goods;
import com.songlanyun.modules.goods.entity.GoodsSku;
import com.songlanyun.modules.goods.model.vo.SkuComItemVo;
import com.songlanyun.modules.goods.service.GoodsService;
import com.songlanyun.modules.goods.service.GoodsSkuService;
import com.songlanyun.modules.marketing.groupbuy.entity.GroupBuyActivity;
import com.songlanyun.modules.marketing.groupbuy.entity.GroupBuyActivityGoods;
import com.songlanyun.modules.marketing.groupbuy.service.GroupBuyActivityGoodsService;
import com.songlanyun.modules.marketing.seckill.dao.SeckillActivityGoodsDao;
import com.songlanyun.modules.marketing.seckill.entity.SeckillActivity;
import com.songlanyun.modules.marketing.seckill.entity.SeckillActivityGoods;
import com.songlanyun.modules.marketing.seckill.entity.SeckillActivityTimeItem;
import com.songlanyun.modules.marketing.seckill.entity.SeckillGoodsSku;
import com.songlanyun.modules.marketing.seckill.model.dto.SeckillActivityGoodsDTO;
import com.songlanyun.modules.marketing.seckill.model.vo.SeckillActivityAppVO;
import com.songlanyun.modules.marketing.seckill.model.vo.SeckillActivityGoodsVO;
import com.songlanyun.modules.marketing.seckill.model.vo.SeckillActivityVO;
import com.songlanyun.modules.marketing.seckill.service.SeckillActivityGoodsService;
import com.songlanyun.modules.marketing.seckill.service.SeckillActivityService;
import com.songlanyun.modules.marketing.seckill.service.SeckillActivityTimeItemService;
import com.songlanyun.modules.marketing.seckill.service.SeckillGoodsSkuService;
import com.songlanyun.modules.order.entity.ChildOrder;
import com.songlanyun.modules.order.entity.Order;
import com.songlanyun.modules.singlegoods.entity.ProductSingleGoodsEntity;
import com.songlanyun.modules.singlegoods.service.ProductSingleGoodsService;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.Range;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.TimeUnit;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("seckillActivityGoodsService")
public class SeckillActivityGoodsServiceImpl extends ServiceImpl<SeckillActivityGoodsDao, SeckillActivityGoods> implements SeckillActivityGoodsService {

    @Resource
    private SeckillGoodsSkuService seckillGoodsSkuService;

    @Resource
    private SeckillActivityTimeItemService seckillActivityTimeItemService;

    @Resource
    private GoodsService goodsService;

    @Resource
    private GoodsSkuService goodsSkuService;

    @Resource
    private SeckillActivityService seckillActivityService;

    @Resource
    private SemaphoreTool semaphoreTool;

    @Resource
    private SeckillActivityGoodsService seckillActivityGoodsService;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ProductSingleGoodsService productSingleGoodsService;


    @Override
    public PageUtils goodsPage(Map<String, Object> params) {

        IPage<SeckillActivityGoodsVO> page = this.baseMapper.goodsPage(new Query<SeckillActivityGoodsVO>().getPage(params), params);

        return new PageUtils(page);
    }

    private SeckillActivityGoodsVO mapToSAGoodsVo(Map<String, Object> params) {
        SeckillActivityGoodsVO vo = new SeckillActivityGoodsVO();
        String title = MapUtil.getStr(params, "title");
        if (StringUtils.isNotBlank(title)) {
            vo.setTitle(title);
        }

        Long seckillId = MapUtil.getLong(params, "seckill_id");
        if (seckillId != null) {
            vo.setSeckillId(seckillId);
        }

        return vo;

    }


    @Override
    @Transactional
    public void saveGoods(SeckillActivityGoodsDTO dto) {
        //如果该秒杀活动上架了该商品，则是修改
        SeckillActivityGoods seckillGoods = this.getByGoodsIdAndSeckillId(dto.getGoodsId(), dto.getSeckillId());
        if (seckillGoods == null) {
            seckillGoods = new SeckillActivityGoods();
        }
        BeanUtil.copyPropertiesIgnoreNull(dto, seckillGoods);//拷贝
        seckillGoods.setPriceType(MarketingConstant.PriceType.getByCode(dto.getPriceType()));//活动打折方式

        //保存商品的规格
        Goods goods = goodsService.getById(seckillGoods.getGoodsId(), true);
        seckillGoods.setShopId(goods.getShopId());//发布商家
        seckillGoods.setSkuTableValue(goods.getSkuTableValue());

        //可售数量+初始化销量>=已售数量
        if ((seckillGoods.getRealStockTotal() + seckillGoods.getInitSaleNum()) < seckillGoods.getResultSaleNum()) {
            throw new RRException(MarketingException.SALE_EXCEPTION);
        }

        //保存
        this.saveOrUpdate(seckillGoods);

        //查询秒杀活动商品sku是否有记录
        Long seckillGoodsId = seckillGoods.getId();
        seckillGoodsSkuService.deleteBySeckillGoodsId(seckillGoodsId);

        //保存秒杀活动商品sku
        List<SeckillGoodsSku> seckillSkuList = dto.getSeckillGoodsSkuList();
        List<SeckillGoodsSku> removeSkuList = new ArrayList<>();
        for (SeckillGoodsSku s : seckillSkuList) {
            //查询商品sku
            GoodsSku goodsSku = goodsSkuService.loadByGoodsAndSkuHashCode(dto.getGoodsId(), s.getSkuHashCode());
            if (goodsSku == null) {

                //万一商品sku未找到,则不添加，跳过，防止报错
                removeSkuList.add(s);
                continue;
            }
            BeanUtil.copyPropertiesIgnoreNull(goodsSku, s);
            if (s.getActivityPrice().compareTo(goodsSku.getSalePrice()) > 0) {
                //拼团活动商品的活动价不能小于商品销售价
                throw new RRException(MarketingException.GROUP_BUY_ACTIVITY_GOODS_PRICE_ERROR);
            }

            if(s.getActivityPrice().compareTo(BigDecimal.ZERO)<=0)
            {
                throw new RRException("秒杀活动价格不能小于等于0");
            }
            //重置id
            s.setId(null);
            s.setSeckillGoodsId(seckillGoodsId);

        }


        seckillSkuList.removeAll(removeSkuList);//删除不存在的sku
        if(CollectionUtil.isEmpty(seckillSkuList))
        {
            throw new RRException(MarketingException.GROUP_BUY_ACTIVITY_GOODS_SKU_ERROR);
        }

        //批量
        seckillGoodsSkuService.saveBatch(seckillSkuList);

        //更新秒杀活动商品的最大、最小价格
        //最小价格
        BigDecimal minPrice = seckillSkuList.stream().map(SeckillGoodsSku::getActivityPrice).min(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO);
        //最大价格
        BigDecimal maxPrice = seckillSkuList.stream().map(SeckillGoodsSku::getActivityPrice).max(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO);

        seckillGoods.setMinPrice(minPrice);
        seckillGoods.setMaxPrice(maxPrice);

        seckillGoods.setMinSalePrice(goods.getMinSalePrice());//最小销售价
        seckillGoods.setMaxSalePrice(goods.getMaxSalePrice());//最大销售价

        //更新销量和库存
        seckillGoods.computeResultSaleAndResidueStock();

        this.baseMapper.updateById(seckillGoods);

        //创建信号量
        createAndSetSemaphore(seckillGoods);

    }

    @Override
    public SeckillActivityGoods getByGoodsIdAndSeckillId(Long goodsId, Long seckillId) {

        SeckillActivityGoods seckillActivityGoods = this.baseMapper.selectOne(
                new LambdaQueryWrapper<SeckillActivityGoods>()
                        .eq(SeckillActivityGoods::getGoodsId, goodsId)
                        .eq(SeckillActivityGoods::getSeckillId, seckillId)
                        .last("limit 1")
        );
        return seckillActivityGoods;
    }

    /**
     * @param id 秒杀活动商品id
     * @return
     */
    @Override
    public SeckillActivityGoodsVO getSeckillGoodsById(Long id, Boolean isThrow) {
        SeckillActivityGoodsVO seckillGoodsInfo = this.baseMapper.getSeckillGoodsInfo(id);
        if (seckillGoodsInfo == null) {
            if (isThrow) {
                throw new RRException(MarketingException.SECKILL_ACTIVITY_GOODS_NOT_EXIST);
            }
        } else {
            seckillGoodsInfo.loadDiscount();
        }

        seckillGoodsInfo.getStartTime();

        return seckillGoodsInfo;
    }

    @Override
    public SeckillActivityGoods loadActivityGoodsById(Long id, Boolean tw) {
        SeckillActivityGoods goods = this.baseMapper.selectById(id);
        if (goods == null && tw) {
            throw new RRException(MarketingException.SECKILL_ACTIVITY_GOODS_NOT_EXIST);
        }
        return goods;
    }

    /**
     * 删除秒杀活动商品
     *
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(Long id) {
        //秒杀活动只要开始了，就不能被删除
        SeckillActivityGoods goods = this.loadActivityGoodsById(id, true);
        /**
         * 2024年2月26日 客户要求 不管是否在进行中 想删就删
         */
//        SeckillActivity activity = seckillActivityService.getById(goods.getSeckillId());
//        Date startDate = DateUtils.stringToDate(DateCustomUtils.dateToString(activity.getStartTime()) + " " + activity.getStartHour());
//        Date endDate = DateUtils.stringToDate(DateCustomUtils.dateToString(activity.getEndTime()) + " " + activity.getEndHour());
//        long timeMillis = System.currentTimeMillis();
//        if (timeMillis > startDate.getTime() && timeMillis <= endDate.getTime()) {
//            throw new RRException(MarketingException.SECKILL_ACTIVITY_START);
//        }

        //1、删除秒杀活动商品
        this.baseMapper.deleteById(id);

        //2、删除秒杀活动商品sku
        seckillGoodsSkuService.deleteBySeckillGoodsId(id);

        //删除秒杀的信号量
        redisUtils.delete(SeckillActivityGoodsService.getSemaphoreKey(goods.getSeckillId(), goods.getId(), goods.getGoodsId()));
    }

    @Override
    public List<SeckillActivityGoods> getBySeckillId(Long seckillId) {
        List<SeckillActivityGoods> list = this.baseMapper.listBySeckillId(seckillId);
        list.forEach(v -> {
            //设置秒杀活动商品ku
            v.setSeckillGoodsSkuList(seckillGoodsSkuService.getBySeckillGoodsId(v.getId()));
        });
        return list;
    }

    /**
     * 正在秒杀的活动商品
     *
     * @param
     * @return
     */
    @Override
    public SeckillActivityVO seckillingList(Long shopId) {

        //正在秒杀的活动商品
        List<SeckillActivityGoodsVO> list = this.baseMapper.seckillingList(shopId);
        SeckillActivityVO seckillActivityVO = new SeckillActivityVO();
        seckillActivityVO.setSeckillActivityGoodsList(list);


        if (list != null && list.size() > 0) {
            //结束时间
            Long finishTime = seckillActivityService.finishTime(shopId);
            seckillActivityVO.setFinishTime(finishTime);

            list.forEach(SeckillActivityGoodsVO::loadDiscount);
        }

        return seckillActivityVO;
    }

    @Override
    public List<SeckillActivityGoodsVO> willSeckillList(Long shopId) {
        Date date = new Date();
        //查询当前正在秒杀的活动
        SeckillActivityTimeItem timeItem = seckillActivityTimeItemService.getOne(
                new LambdaQueryWrapper<SeckillActivityTimeItem>()
                        .eq(SeckillActivityTimeItem::getShopId, shopId)
                        .le(SeckillActivityTimeItem::getStartTime, date)
                        .ge(SeckillActivityTimeItem::getEndTime, date)
                        .last("limit 1")
        );
        //取出开始时间，用于查询下一场活动
        Date startTime = new Date();
        if (timeItem != null) {
            startTime = timeItem.getStartTime();
        }

        List<SeckillActivityGoodsVO> list = this.baseMapper.willSeckillList(shopId, startTime);
        list.forEach(SeckillActivityGoodsVO::loadDiscount);
        return list;
    }

    /**
     * 增加销量
     *
     * @param order
     * @return
     */
    @Override
    public void addSaleNum(Order order) {
        ChildOrder childOrder = order.getChildOrderList(true).get(0);
        RLock lock = redissonClient.getLock(LockPrefixConstant.SECKILL_GOODS + childOrder.getActivityGoodsId());
        lock.lock(2, TimeUnit.SECONDS);
        try {
            SeckillActivityGoods seckillActivityGoods = this.loadActivityGoodsById(childOrder.getActivityGoodsId(), true);
            seckillActivityGoods.incSaleNum(childOrder.getNumber());
            this.updateById(seckillActivityGoods);
        } finally {
            lock.unlock();
        }

    }

    /**
     * 减少销量
     *
     * @param order
     * @return
     */
    @Override
    public void decSaleNum(Order order) {
        ChildOrder childOrder = order.getChildOrderList(true).get(0);
        RLock lock = redissonClient.getLock(LockPrefixConstant.SECKILL_GOODS + childOrder.getActivityGoodsId());
        lock.lock(2, TimeUnit.SECONDS);
        try {
            SeckillActivityGoods seckillActivityGoods = this.loadActivityGoodsById(childOrder.getActivityGoodsId(), true);
            seckillActivityGoodsService.release(seckillActivityGoods, childOrder.getNumber());
            seckillActivityGoods.decSaleNum(childOrder.getNumber());
            this.updateById(seckillActivityGoods);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获得销量
     *
     * @param activityGoodsId
     * @return
     */
    @Override
    public Integer getStock(Long activityGoodsId) {
        SeckillActivityGoods seckillActivityGoods = this.loadActivityGoodsById(activityGoodsId, true);
        return seckillActivityGoods.getResidueStockTotal();
    }

    /**
     * 支付过期回退库存
     *
     * @param order
     */
    @Override
    public void payExpireHandler(Order order) {
        this.decSaleNum(order);
    }

    /**
     * 判断当前时间段有没有指定秒杀活动的商品存在
     *
     * @param serkillId 秒杀活动id
     * @param goodsId   商品id
     * @return
     */
    @Override
    public Boolean verifyBySerkillAndGoods(Long serkillId, Long goodsId) {
        return this.baseMapper.verifyBySerkillAndGoods(serkillId, goodsId);

    }

    /**
     * 判断当前时间段有没有指定秒杀活动商品存在
     *
     * @param serkillGoodsId 秒杀活动商品id
     * @return
     */
    @Override
    public Boolean verifyBySerkillGoods(Long serkillGoodsId) {
        return this.baseMapper.verifyBySerkillGoods(serkillGoodsId);
    }

    /**
     * 创建信号量并设置容量
     *
     * @param goods 秒杀商品
     * @return
     */
    @Override
    public void createAndSetSemaphore(SeckillActivityGoods goods) {
        String key = SeckillActivityGoodsService.getSemaphoreKey(goods.getSeckillId(), goods.getId(), goods.getGoodsId());
        Integer num = redisUtils.get(key, Integer.class);
        if (num != null) {
            redisUtils.delete(key);
        }
        semaphoreTool.setRSemaphorePermits(key, goods.getResidueStockTotal());
    }


    /**
     * 获取当前的信号
     *
     * @param goods
     * @return
     */
    @Override
    public RSemaphore getSemaphore(SeckillActivityGoods goods) {
        return semaphoreTool.getRSemaphore(SeckillActivityGoodsService.getSemaphoreKey(goods.getSeckillId(), goods.getId(), goods.getGoodsId()));
    }

    /**
     * 获取信号量，如果获取成功信号号会减少
     *
     * @param activityId
     * @param activityGoodsId
     * @param goodsId
     * @param size
     * @return
     */
    @Override
    public Boolean tryAcquire(Long activityId, Long activityGoodsId, Long goodsId, int size) {
        return semaphoreTool.tryAcquire(SeckillActivityGoodsService.getSemaphoreKey(activityId, activityGoodsId, goodsId), size);
    }


    /**
     * 释放（回退）信号量,
     *
     * @param goods
     * @param size
     */
    @Override
    public void release(SeckillActivityGoods goods, int size) {
        semaphoreTool.release(SeckillActivityGoodsService.getSemaphoreKey(goods.getSeckillId(), goods.getId(), goods.getGoodsId()), size);
    }

    /**
     * 释放（回退）信号量,
     *
     * @param activityId
     * @param activityGoodsId
     * @param goodsId
     * @param size
     */
    @Override
    public void release(Long activityId, Long activityGoodsId, Long goodsId, int size) {
        semaphoreTool.release(SeckillActivityGoodsService.getSemaphoreKey(activityId, activityGoodsId, goodsId), size);

    }

    @Override
    public List<SeckillGoodsSku> getSkusById(Long seckillGoodsId) {
        SeckillActivityGoods goods = this.baseMapper.selectById(seckillGoodsId);
        //查询sku
        List<SeckillGoodsSku> skus = seckillGoodsSkuService.getBySeckillGoodsId(seckillGoodsId);

        skus.forEach(s -> {
            ProductSingleGoodsEntity singleGoods = productSingleGoodsService.getByShopIdAndId(goods.getShopId(), s.getSingleGoodsId());
            Integer ableStock = 0;
            if (singleGoods != null) {
                //库存
                ableStock = singleGoods.getAbleStock();
            }
            s.setAbleStock(ableStock);

        });
        return skus;
    }

    @Override
    public SeckillActivityAppVO flashSale(Map<String, Object> params) {
        SeckillActivityAppVO vo = new SeckillActivityAppVO();
        //秒杀活动结束时间戳
        SeckillActivityTimeItem timeItem = seckillActivityTimeItemService.getNowOne(params);
        if (timeItem != null) {
            //开始时间场次
            String startHour = timeItem.getStartHour();
            vo.setStartHour(startHour.split(":")[0]);
            //结束时间
            vo.setFinishTime(timeItem.getEndTime().getTime());

            //当前时间段所有秒杀活动商品
            List<SeckillActivityGoodsVO> list = this.baseMapper.getNowAllSeckillGoods(params);
            vo.setList(list);
        }

        return vo;
    }


    @Override
    public SeckillActivityAppVO startFlashSale(Map<String, Object> params) {
        SeckillActivityAppVO vo = new SeckillActivityAppVO();
        String startHour = MapUtil.getStr(params, "start_hour");
        String endHour = MapUtil.getStr(params, "end_hour");
        Long shopId = MapUtil.getLong(params, "shop_id");
        if (StringUtils.isBlank(startHour) || StringUtils.isBlank(endHour)) {
            throw new RRException(MarketingException.INCORRECT_PARAMETER_PASSED_IN);
        }
        //活动开始时间
        String startTime = DateCustomUtils.dateToString(new Date()) + " " + startHour;
        //活动结束时间
        String endTime = DateCustomUtils.dateToString(new Date()) + " " + endHour;

        long nowTime = System.currentTimeMillis();
        long startTimestamp = DateUtils.stringToDate(startTime).getTime();
        long endTimestamp = DateUtils.stringToDate(endTime).getTime();

        vo.setStartHour(startHour);
        //当前时间
        vo.setNowTime(nowTime);
        //结束时间
        vo.setFinishTime(endTimestamp);

        //设置活动状态 -- 计算当前时间的活动状态
        if (nowTime < startTimestamp) {
            //即将开始
            vo.setActiveState(MarketingConstant.ActiveState.NOT_START);
        } else if (nowTime > endTimestamp) {
            //已过期
            vo.setActiveState(MarketingConstant.ActiveState.FINISHED);
        } else {
            //正在抢购
            vo.setActiveState(MarketingConstant.ActiveState.UNDERWAY);
        }

        //查询当前开始时间段的活动
        SeckillActivityTimeItem timeItem = seckillActivityTimeItemService.currentTimeOneSeckill(startTime, shopId);

        if (timeItem != null) {
            //查询秒杀活动
            SeckillActivity seckillActivity = seckillActivityService.getById(timeItem.getSeckillId());
            if (seckillActivity == null) {
                throw new RRException(MarketingException.SECKILL_ACTIVITY_NOT_EXIST);
            }

//            //结束时间
//            vo.setFinishTime(timeItem.getEndTime().getTime());
            //当前场次所有的活动商品
            IPage<SeckillActivityGoodsVO> page = this.baseMapper.startFlashSalePage(new Query<SeckillActivityGoodsVO>().getPage(params), startTime, shopId);
            vo.setPage(new PageUtils(page));
            vo.setName(seckillActivity.getName());
            vo.setImgUrl(seckillActivity.getImgUrl());
        }

        return vo;
    }


    @Override
    public void verifyActiveGoods(Long seckillId) {
        Integer count = this.baseMapper.selectCount(
                new LambdaQueryWrapper<SeckillActivityGoods>()
                        .eq(SeckillActivityGoods::getSeckillId, seckillId)
        );

        if (count > 0) {
            throw new RRException(MarketingException.SECKILL_ACTIVITY_GOODS_EXIST);
        }
    }

    @Override
    public List<SeckillActivityGoodsVO> seckillingGoodsList(Long shopId) {
        return this.baseMapper.seckillingList(shopId);
    }


}
